package com.yyplatform.frontend.infra.web.interceptor;

import java.io.IOException;
import java.io.PrintWriter;

import org.apache.commons.lang3.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.yyplatform.frontend.infra.web.annotation.LoginRequired;
import com.yyplatform.frontend.infra.web.annotation.WeekLoginRequired;
import com.yyplatform.frontend.infra.exception.ErrorCode;
import com.yyplatform.frontend.infra.exception.ServiceException;
import com.yyplatform.frontend.infra.web.jwt.CurrentUser;
import com.yyplatform.frontend.infra.web.jwt.CurrentUserHolder;
import com.yyplatform.frontend.infra.web.jwt.JwtTokenService;
import com.yyplatform.frontend.infra.model.RestResponse;
import com.yyplatform.frontend.infra.service.UserInfraService;

import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;

/**
 * Created on 2024-10-17
 */
@Slf4j
public class JwtTokenCheckInterceptor implements HandlerInterceptor {

    public JwtTokenCheckInterceptor(UserInfraService accountInfraService1,
            JwtTokenService jwtTokenService1) {
        this.userInfraService = accountInfraService1;
        this.jwtTokenService = jwtTokenService1;
    }

    private final UserInfraService userInfraService;

    private final JwtTokenService jwtTokenService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        //这个地方和前端约定，要求前端将jwtToken放在请求的Header部分

        //所以以后发起请求的时候就需要在Header中放一个Authorization，值就是对应的Token
        String jwt = request.getHeader("Authorization");

        if (StringUtils.isEmpty(jwt)) {
            // jwt = CookieUtil.getVal(request, "jwtToken");
        }

        if (StringUtils.isEmpty(jwt)) {
            jwt = request.getParameter("jwtToken");
        }

        //        log.info("请求的 Header 中藏有 jwtToken {}", jwt);

        // request.setAttribute("jwtToken", jwt);


        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            LoginRequired loginRequired = handlerMethod.getMethodAnnotation(LoginRequired.class);
            if (loginRequired != null) {
                try {
                    boolean check = jwtTokenService.isVerify(jwt);
                    if (check) {
                        setLoginUser(jwt, request, response);
                        return true;
                    } else {
                        sendErrorMsg(response);
                        return false;
                    }
                } catch (Exception ex) {
                    sendErrorMsg(response);
                    return false;
                }
            }

            WeekLoginRequired weekLoginRequired = handlerMethod.getMethodAnnotation(WeekLoginRequired.class);
            if (weekLoginRequired != null) {
                try {
                    if (jwtTokenService.isVerify(jwt)) {
                        setLoginUser(jwt, request, response);
                    }
                } catch (Exception ex) {
                    // ignore
                }
                return true;
            }
        }


        return true;
    }

    private void sendErrorMsg(HttpServletResponse response) throws IOException {
        response.setContentType("application/json; charset=utf-8");
        PrintWriter writer = response.getWriter();
        ObjectWriter objectWriter = new ObjectMapper().writer().withDefaultPrettyPrinter();
        writer.print(objectWriter.writeValueAsString(RestResponse.ok()));
        writer.close();
        response.flushBuffer();
    }

    private void setLoginUser(String jwt, HttpServletRequest request, HttpServletResponse response) {
        Claims claims = jwtTokenService.decode(jwt);
        //        log.info("claims ===> [{}] ", JSONUtil.toJsonStr(claims));
        long id = Long.parseLong(claims.getSubject());
        try {
            // 这里通过id可能找不到对应的user，可能是有问题的。
            CurrentUser currentUser = userInfraService.getById(id);

            Integer loginTypeStr = claims.get("loginType", Integer.class);
            Long userId = claims.get("userId", Long.class);
            String identifier = claims.get("identifier", String.class);


            if (loginTypeStr == null || StringUtils.isEmpty(identifier) || userId == null) {
                throw ServiceException.of(ErrorCode.UNAUTHORIZED);
            }

            if (id != userId) {
                throw ServiceException.of(ErrorCode.UNAUTHORIZED);
            }

            boolean checkAuth = userInfraService.checkAuth(userId, loginTypeStr, identifier);
            if (!checkAuth) {
                throw ServiceException.of(ErrorCode.UNAUTHORIZED);
            }

            CurrentUserHolder userHolder = new CurrentUserHolder(currentUser, jwt);
            request.setAttribute(CurrentUserHolder.HOLDER_KEY, userHolder);
        } catch (Exception ex) {
            log.error("通过jwt token解析的userId没找到用户. id=[{}], jwt=[{}]", id, jwt, ex);
            throw ServiceException.of(ErrorCode.UNAUTHORIZED);
        }
    }

}
